package com.heima.search.service.impl;

import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.search.dto.UserSearchDto;
import com.heima.search.service.ApUserSearchService;
import com.heima.search.service.UserSearchService;
import com.heima.search.utils.AppUserThreadLocalUtil;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class UserSearchServiceImpl implements UserSearchService {

    @Resource
    private RestHighLevelClient restHighLevelClient;

    @Resource
    private ApUserSearchService apUserSearchService;

    @Override
    public ResponseResult search(UserSearchDto dto) {
        //1.校验参数
        if (dto.getPageSize() <= 0) {
            dto.setPageSize(10);
        }
        dto.setPageSize(Math.min(dto.getPageSize(), 50));

        if (dto.getMinBehotTime() == null) {
            dto.setMinBehotTime(new Date());
        }


        //保存用户搜索历史纪录   只有在第一页的时候需要保存搜索历史记录
        if (dto.getFromIndex()==0 && AppUserThreadLocalUtil.get()!=null) {
            apUserSearchService.saveUsersearch(dto.getSearchWords(),AppUserThreadLocalUtil.get().getId());
        }



        //2.创建请求
        SearchRequest request = new SearchRequest("app_article_info");
        //3.准备DSL
        //3.1查询方式   boolQuery----->查询条件：  关键词匹配，最小时间范围查询
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //3.1.1封装关键词匹配查询条件  -- 需要参与相关性算分，使用 must
        if (StringUtils.isNotEmpty(dto.getSearchWords())) {
            boolQuery.must(QueryBuilders.multiMatchQuery(dto.getSearchWords(), "title", "content"));
        }

        //3.1.2封装时间范围查询条件 ---- 不需要进行相关性算分，所以使用filter效率更高
        boolQuery.filter(QueryBuilders.rangeQuery("publishTime").lte(dto.getMinBehotTime()));

        request.source().query(boolQuery);
        //3.2高亮参数
        HighlightBuilder highlightBuilder=new HighlightBuilder();
        highlightBuilder.preTags("<font style='color:red; font-size: inherit;'>");
        highlightBuilder.postTags("</font>");
        highlightBuilder.field("title");
        request.source().highlighter(highlightBuilder);
        //3.3数据排序    ----在ES中如果指定了排序字段，这个时候算分会失效
        request.source().sort("publishTime", SortOrder.DESC);
        //3.4分页参数
        request.source().size(dto.getPageSize());
        //4.发送请求
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR,"执行关键词查询出错");
        }
        //5.解析数据
        //5.1 获取命中数据
        SearchHits hits = response.getHits();
        //5.2获取命中数据总条数
        Long count = hits.getTotalHits().value;
        //5.3获取命中数据列表
        SearchHit[] searchHits = hits.getHits();
        //5.4便利命中数据列表，封装数据返回
        List<Map> results = Arrays.stream(searchHits).map(searchHit -> {
            //5.4.1获取命中数据中的文章数据（json对象）
            String sourceAsString = searchHit.getSourceAsString();
            Map map = JSON.parseObject(sourceAsString, Map.class);
            //5.4.2获取高亮数据
            Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
            if (!MapUtil.isEmpty(highlightFields)) {
                HighlightField highlightField = highlightFields.get("title");
                if (highlightField != null) {
                    Text[] text = highlightField.getFragments();
                    String highlight_title = StringUtils.join(text);
                    map.put("h_title",highlight_title);
                }
            }else {
                map.put("h_title", map.get("title"));
            }
            return map;

        }).collect(Collectors.toList());

        //6.封装数据返回
        ResponseResult result=new PageResponseResult(1,dto.getPageSize(),count.intValue());
        result.setData(results);
        return result;
    }
}
